package com.registry.ser.service;

import com.registry.ser.constant.RegisterConstant;
import com.registry.ser.instance.ServiceInstance;
import com.registry.ser.request.RegistryParamModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class RegistryParamServiceImpl {

    /**
     * juc
     * set底层如何实现？ HashMap集合
     * HashSet--无序
     */
    private static volatile Map<String, HashMap<String, ServiceInstance>> mapServiceInstances = new ConcurrentHashMap<>();

    private ScheduledExecutorService scheduledService = Executors.newSingleThreadScheduledExecutor();

     /**
     * 该类加入容器中，默认是单例，所以构造方法只会执行一次
     * 构造方法中写定时检测服务状态
      * scheduleAtFixedRate： 当到达3秒后，检测上一次任务是否结束，如果结束，立即执行本次任务，如果没结束，等到上次任务结束再立即执行
      * scheduleAtFixedRate： 当到达3秒后立即执行本次任务，无论上次是否完成
     */
    public RegistryParamServiceImpl(){
        // 启动3秒后执行第一次，然后3秒一轮询
//        scheduledService.scheduleAtFixedRate(new RegistryExecutors(), 3, 3, TimeUnit.SECONDS);
        scheduledService.scheduleWithFixedDelay(new RegistryExecutors(), 3, 3, TimeUnit.SECONDS);
    }

    /**
     * 服务注册
     * @param registryParamModel
     * @return
     */
    public boolean serverRegistry(RegistryParamModel registryParamModel) {

        try {
            // 有限校验该服务是否存在
            String serviceName = registryParamModel.getServiceName();
            HashMap<String, ServiceInstance> serviceInstances = mapServiceInstances.get(serviceName);
            if(serviceInstances == null){
                // 该服务之前不存在，双重锁创建一个新的HashSet集合
                synchronized (this){
                    if(serviceInstances == null){
                        // 创建已给新HashSet集合来保存这个服务
                        serviceInstances = new HashMap<String, ServiceInstance>();
                        mapServiceInstances.put(serviceName, serviceInstances);
                    }
                }
            }
            // 把新注册的服务加到HashSet中，如果之前就存在，说明这个就是集群,服务名相同，地址不同。
            ServiceInstance serviceInstance = new ServiceInstance(serviceName, registryParamModel.getServiceAddres(), System.currentTimeMillis());
            log.info("新服务注册：服务名 = {}, 服务地址:{}", registryParamModel.getServiceName(), registryParamModel.getServiceAddres());
            serviceInstances.put(registryParamModel.getServiceAddres(), serviceInstance);
            return true;
        }catch (Exception e){
            return false;
        }
    }

    /**
     * 服务下线
     * @param registryParamModel
     * @return
     */
    public boolean serverRemove(RegistryParamModel registryParamModel) {

        final boolean[] result = {false};
        // 校验该服务是否存在
        HashMap<String, ServiceInstance> serviceInstances = mapServiceInstances.get(registryParamModel.getServiceName());
        if(serviceInstances != null){
            serviceInstances.remove(registryParamModel.getServiceAddres());
        }
        log.info("服务注销：服务名 = {}, 服务地址:{}", registryParamModel.getServiceName(), registryParamModel.getServiceAddres());
        return result[0];
    }

    /**
     * 服务续约
     * @param registryParamModel
     * @return
     */
    public boolean serverRenewal(RegistryParamModel registryParamModel) {
        // 1. 获取对应服务的hashSet
        HashMap<String, ServiceInstance> serviceInstances = mapServiceInstances.get(registryParamModel.getServiceName());
        // 2.判断是否存在该服务
        if(serviceInstances == null){
            // 3. 如果不存在该服务，需要重新把该服务注册进去,刚注册后会直接把当前时间赋值到最后一次续约时间
            return serverRegistry(registryParamModel);
        }
        // 如果服务存在，遍历具体是哪个ip地址来续约的
        serviceInstances.forEach((k,v) -> {
            if(serviceInstances.get(k).getServiceAddres().equalsIgnoreCase(registryParamModel.getServiceAddres())){
                // 用当前时间加上续约时间
                v.setLastRenewalTime(System.currentTimeMillis() + registryParamModel.getRenewalDuration());
            }
        });
        return true;
    }

    /**
     * 查看该服务集群列表
     * @param registryParamModel
     * @return
     */
    public List<ServiceInstance> serverSelect(RegistryParamModel registryParamModel) {
        List<ServiceInstance> serviceInstanceList = new ArrayList<>();
        HashMap<String, ServiceInstance> serviceInstances = mapServiceInstances.get(registryParamModel.getServiceName());
        if(serviceInstances != null){
            serviceInstances.forEach((k,v) -> {
                serviceInstanceList.add(v);
            });
        }else {
            return null;
        }
        return serviceInstanceList;
    }


    /**
     * 检测已注册服务状态内部类
     */
    class RegistryExecutors implements Runnable{
        @Override
        public void run() {
            List<String> removeOutKey = new ArrayList<>();
            // 1. 遍历容器
            mapServiceInstances.forEach((k,v)->{
                List<String> removeInnerKey = new ArrayList<>();
                // 2.取出每个服务 对应集群地址, 并判断hashset长度是否为0，如果为0，直接remove即可
                HashMap<String, ServiceInstance> serviceInstances = mapServiceInstances.get(k);
                if(serviceInstances.size() == 0){
                    removeOutKey.add(k);
                }
                // 3. 遍历每个服务的集群，最终所有服务都会遍历
                serviceInstances.forEach((key,value) -> {
                    long currentTime = System.currentTimeMillis();
                    long lastRenewalTime = value.getLastRenewalTime();
                    // 4. 校验最后一次续约时间 和 当前时间对比是否大于10秒
                    if(currentTime - lastRenewalTime > RegisterConstant.DELETEIP_TIMEOUT){
                        log.info("服务名称：" + value.getServiceName() + "," +
                                "服务地址" + value.getServiceAddres() + ",当前系统时间:" + currentTime
                                + ",该服务上一次续约时间" + lastRenewalTime + "服务被剔除.");
                        // 超时：剔除服务
                        removeInnerKey.add(key);
                    }
                });
                // 移除内层map元素
                removeInnerKey.stream().forEach(outKey ->{
                    serviceInstances.remove(outKey);
                });

            });

            // 移除外层map元素
            removeOutKey.stream().forEach(outKey ->{
                mapServiceInstances.remove(outKey);
            });
        }
    }
}
